#ifndef SLICECOLLECTION_H
#define SLICECOLLECTION_H

#ifndef FALLOC_H
# error must include falloc
#endif
#ifndef CORE_H
# include "..\..\..\Core\System\Core.h"
#endif
#include "Limits.h"
#include "Slice.hpp"
#include "HibernateCollection.hpp"
#include "SliceFractionCache.hpp"
#include "SliceFractionCollection.hpp"
#include "ElementCollection.hpp"

namespace System { namespace Scheduler {

	class SliceCollection
	{
	private:
		fallocHeap* _heap;
		ulong _currentSlice;
		char _currentHibernate;
        Slice _slices[EngineSettings__MaxTimeslices];
        HibernateCollection _hibernates;
        SliceFractionCache _fractionCache;

	public:
		__device__ void xtor(fallocHeap* heap)
		{
			trace(SliceCollection, "xtor");
			_heap = heap;
			_currentSlice = 0;
			_currentHibernate = 0;
			for (int sliceIndex = 0; sliceIndex < EngineSettings__MaxTimeslices; sliceIndex++)
				_slices[sliceIndex].xtor(_heap);
			_hibernates.xtor(_heap);
			_fractionCache.xtor(_heap);
		}
		__device__ void Dispose()
		{
			trace(SliceCollection, "Dispose");
			for (int sliceIndex = 0; sliceIndex < EngineSettings__MaxTimeslices; sliceIndex++)
				_slices[sliceIndex].Dispose();
			_hibernates.Dispose();
			_fractionCache.Dispose();
		}

        __device__ void Schedule(Element* element, ulong time)
        {
            trace(SliceCollection, "Schedule %d", TimePrec__DecodeTime(time));
            ulong slice = (ulong)(time >> TimePrec__TimePrecisionBits);
            ulong fraction = (ulong)(time & TimePrec__TimePrecisionMask);
            if (slice < EngineSettings__MaxTimeslices)
            {
                // first fraction
                if (slice == 0)
                    _fractionCache.EnsureCache(fraction);
                // roll timeslice for index
                slice += _currentSlice;
                if (slice >= EngineSettings__MaxTimeslices)
                    slice -= EngineSettings__MaxTimeslices;
                _slices[slice].Fractions.Schedule(element, fraction);
            }
            else
                _hibernates.Hibernate(element, time);
        }

        __device__ void MoveNextSlice()
        {
			trace(SliceCollection, "MoveNextSlice %d", _currentSlice);
			_slices[_currentSlice].Dispose();
            if (++_currentSlice >= EngineSettings__MaxTimeslices)
            {
                _currentSlice = 0;
                _hibernates.DeHibernate(this);
                if (++_currentHibernate >= EngineSettings__HibernatesTillReShuffle)
                {
                    _currentHibernate = 0;
                    _hibernates.ReShuffle();
                }
            }
        }

	};

	__device__ void ElementCollection::DeHibernate(SliceCollection* slices)
    {
		trace(ElementCollection, "DeHibernate");
		ElementList::Enumerator e; _singles.GetEnumerator(e);
		while (e.MoveNext(&_singles))
        {
			Element* single = e.Current;
            ulong time = (ulong)single->Metadata;
            if (time < EngineSettings__MaxTimeslicesTime)
                thrownew(Exception, "paranoia");
            ulong newTime = (ulong)(time -= EngineSettings__MaxTimeslicesTime);
            if (newTime < EngineSettings__MaxTimeslicesTime)
            {
                _singles.Remove(single);
                slices->Schedule(single, newTime);
            }
        }
		LinkedObjList<ElementRef>::Enumerator e2; _multiples.GetEnumerator(e2);
		while (e2.MoveNext(&_multiples))
        {
			ElementRef* multiple = e2.Current;
            ulong time = (ulong)(multiple->Metadata);
            if (time < EngineSettings__MaxTimeslicesTime)
                thrownew(Exception, "paranoia");
            ulong newTime = (ulong)(time -= EngineSettings__MaxTimeslicesTime);
            if (newTime < EngineSettings__MaxTimeslicesTime)
            {
				_multiples.Remove(multiple);
                slices->Schedule(multiple->Element, newTime);
            }
        }
    }

}}

#endif // SLICECOLLECTION_H


	